// linux/kernel/chr_drv/tty_ioctl.c
#include <errno.h>
#include <termios.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>

static unsigned short quotient[]={
	0,	2304,	1536,	1047,	857,
	768,	576,	384,	192,	96,
	64,	48,	24,	12,	6,
	3
};

static void change_speed(struct tty_struct *tty)
{
	unsigned short port, quot;
	if(!(port=tty->read_q.data))
		return;
	quot=quotient[tty->termios.c_cflag&CBAUD];
	cli();
	outb(0x80,port+3);
	outb(quot&0xff, port);
	outb(quot>>8, port+1);
	outb(0x03, port+3);
	sti();
}

static void flush(struct tty_queue *queue)
{
	cli();
	queue->head = queue->tail;
	sti();
}

static void wait_until_sent(struct tty_struct *tty)
{
	// not implemented
}
static void send_break(struct tty_struct *tty)
{
	// not implemented
}
static int get_termios(struct tty_struct *tty, struct termios *termios)
{
	int i;
	verify_area(termios, sizeof(*termios));
	for(i=0;i<(sizeof(*termios));i++)
		put_fs_byte(((char*)&tty->termios)[i],i+(char*)termios);
	return 0;
}

static int set_termios(struct tty_struct *tty, struct termios *termios)
{
	int i;
	for(i=0;i<(sizeof(*termios));i++)
		((char*)&tty->termios)[i]=get_fs_byte(i+(char*)termios);
	change_speed(tty);
	return 0;
}

static int get_termio(struct tty_struct *tty, struct termio *termio)
{
	return 0;
}

static int set_termio(struct tty_struct *tty, struct termio *termio)
{
	return 0;
}

int tty_ioctl(int dev, int cmd, int arg)
{
	struct tty_struct *tty;
	if(MAJOR(dev)==5){
		dev=current->tty;
		if(dev<0)
			panic("tty_ioctl:dev<0");
	}else
		dev=MINOR(dev);
	tty=dev+tty_table;
	switch(cmd){
		case TCGETS:
			return get_termios(tty,(struct termios*)arg);
		case TCSETSF:
			flush(&tty->read_q);
		case TCSETSW:
			wait_until_sent(tty);
		case TCSETS:
			return set_termios(tty, (struct termios*)arg);
		case TCGETA:
			return get_termio(tty,(struct termio*)arg);
		case TCSETAF:
			flush(&tty->read_q);
		case TCSETA:
			return set_termio(tty,(struct termio*)arg);
		case TCSBRK:
			if(!arg){
				wait_until_sent(tty);
				send_break(tty);
			}
			return 0;
		case TCXONC:
			return -EINVAL;
		case TCFLSH:
			if(arg==0)
				flush(&tty->read_q);
			else if(arg==1)
				flush(&tty->write_q);
			else if(arg==2){
				flush(&tty->read_q);
				flush(&tty->write_q);
			}else
				return -EINVAL;
			return 0;
		case TIOCEXCL:
			return -EINVAL;
		case TIOCNXCL:
			return -EINVAL;
		case TIOCSCTTY:
			return -EINVAL;
		case TIOCGPGRP:
			verify_area((void*)arg, 4);
			put_fs_long(tty->pgrp, (unsigned long*)arg);
			return 0;
		case TIOCSPGRP:
			tty->pgrp=get_fs_long((unsigned long *)arg);
			return 0;
		case TIOCOUTQ:
			verify_area((void*)arg,4);
			put_fs_long(CHARS(tty->secondary),(unsigned long*)arg);
			return 0;
		case TIOCSTI:
			return -EINVAL;
		case TIOCGWINSZ:
			return -EINVAL;
		case TIOCSWINSZ:
			return -EINVAL;
		case TIOCMGET:
			return -EINVAL;
		case TIOCMBIS:
			return -EINVAL;
		case TIOCMBIC:
			return -EINVAL;
		case TIOCMSET:
			return -EINVAL;
		case TIOCGSOFTCAR:
			return -EINVAL;
		case TIOCSSOFTCAR:
			return -EINVAL;
		default:
			return -EINVAL;
	}
}

			
	
